/**  
 * Copyright (c) 2008, Noel Lynch All rights reserved. Redistribution and use  
 * in source and binary forms, with or without modification, are permitted  
 * provided that the following conditions are met: Redistributions of source  
 * code must retain the above copyright notice, this list of conditions and the  
 * following disclaimer. Redistributions in binary form must reproduce the above  
 * copyright notice, this list of conditions and the following disclaimer in the  
 * documentation and/or other materials provided with the distribution. Neither  
 * the name of Noel Lynch nor the names of contributors may be used to  
 * endorse or promote products derived from this software without specific prior  
 * written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;  
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR  
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF  
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
 */
// Jul 31, 2008

package com.sceneworker.monitorpopup;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Hashtable;
import java.util.concurrent.Callable;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JMenu;
import javax.swing.tree.TreePath;

import net.miginfocom.swing.MigLayout;

import com.jme.renderer.pass.Pass;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.PassNodeState;
import com.jme.scene.Spatial;
import com.jme.scene.state.RenderState;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.export.Savable;
import com.jme.util.export.binary.BinaryExporter;
import com.jme.util.export.binary.BinaryImporter;
import com.myjavautils.GenericUtils;
import com.myjavautils.swing.MigLayoutUtils;
import com.myjavautils.swing.SimpleAlert;
import com.myjavautils.swing.SwingComponentFactory;
import com.sceneworker.app.globals.SceneWorkerDialogManager;
import com.sceneworker.app.globals.SceneWorkerGlobals;
import com.sceneworker.util.jme.coderep.JMECodeRep;
import com.sceneworker.wizards.WizardBase;
import com.sceneworker.wizards.WizardManager;


public class CoreMenu extends BaseMenu implements ActionListener {
	private static final String COPY_OBJECT_DATA = "Copy Object Data";
	private static final String COPY_OBJECT_REFERENCE = "Copy Object Reference";
	private static final String PASTE_OBJECT = "Paste Object";
	private static final String CUT_OBJECT = "Cut Object";
	private static final String REMOVE = "Remove";
	private static final String SAVE_JME_OBJECT = "Save Object";
	private static final String EXPORT_CODE_REP = "Export Code Representation";
	
	private		Object		mcl_jmeObject;
	private		Object		mcl_parent;

	static		private		Hashtable<Class<?>, ObjectPasteAction>	mcla_pasteActions;

	public
	CoreMenu(TreePath cl_selectedPath, SceneWorkerPopUp cl_menu) {
		super(cl_menu);

		if(mcla_pasteActions == null) {
			mcla_pasteActions = new Hashtable<Class<?>, ObjectPasteAction>();
			registerDefaultPasteActions();

		}

		mcl_jmeObject = cl_selectedPath.getLastPathComponent();
		if(cl_selectedPath.getParentPath() != null) {
			mcl_parent = cl_selectedPath.getParentPath().getLastPathComponent();
		}

		buildMenu();
	}

	static	private		void	registerDefaultPasteActions() {
		mcla_pasteActions.put(Node.class, new ObjectPasteAction() {

			public void pasteObject(Object cl_objectToPaste, Object cl_objectToPasteTo) {
				if(cl_objectToPaste == null) { return; }

				Node		lcl_n = (Node)cl_objectToPasteTo;
				
				if(cl_objectToPaste instanceof Spatial) {
					SceneWorkerGlobals.inst().pasteSpatial(lcl_n, (Spatial)cl_objectToPaste);
					lcl_n.updateGeometricState(0, true);
				}
				else if (cl_objectToPaste instanceof RenderState) {
					lcl_n.setRenderState((RenderState)cl_objectToPaste);
					lcl_n.updateRenderState();
				}
			}

		});

		mcla_pasteActions.put(Spatial.class, new ObjectPasteAction() {

			public void pasteObject(Object cl_objectToPaste, Object cl_objectToPasteTo) {
				if(cl_objectToPaste == null) { return; }

				if (cl_objectToPaste instanceof RenderState) {
					((Spatial)cl_objectToPasteTo).setRenderState((RenderState)cl_objectToPaste);
					((Spatial)cl_objectToPasteTo).updateRenderState();
				}
			}

		});

		mcla_pasteActions.put(PassNodeState.class, new ObjectPasteAction() {

			public void pasteObject(Object cl_objectToPaste, Object cl_objectToPasteTo) {
				if(cl_objectToPaste == null) { return; }

				if(cl_objectToPaste instanceof RenderState) {
					((PassNodeState)cl_objectToPasteTo).setPassState((RenderState)cl_objectToPaste);
				}
			}

		});		
	}

	@Override
	protected void buildMenu() {
		mcl_menu.addSeparator();

		mcl_menu.add(buildAMenuItem(SAVE_JME_OBJECT));

		if((mcl_jmeObject instanceof Spatial) || (mcl_jmeObject instanceof Controller) || (mcl_jmeObject instanceof RenderState)) {
			mcl_menu.add(buildAMenuItem(REMOVE));
		}

		if((mcl_jmeObject instanceof Spatial) || (mcl_jmeObject instanceof RenderState) || (mcl_jmeObject instanceof PassNodeState)) {
			if(mcl_menu.getObjectBuffer() != null) {
				mcl_menu.add(buildAMenuItem(PASTE_OBJECT));
			}

			JMenu		lcl_copyMenu = new JMenu("Copy");
			
			if(mcl_jmeObject instanceof Spatial) {
				lcl_copyMenu.add(buildAMenuItem(COPY_OBJECT_DATA));
			}
			
			lcl_copyMenu.add(buildAMenuItem(COPY_OBJECT_REFERENCE));
			
			mcl_menu.add(lcl_copyMenu);
			
			if(mcl_jmeObject instanceof Spatial){
				mcl_menu.add(buildAMenuItem(CUT_OBJECT));
			}
		}
		
		mcl_menu.add(buildAMenuItem(EXPORT_CODE_REP));
		
		mcl_menu.addSeparator();
		initialiseWizards();
	}
	
	private	void	initialiseWizards() {
		JMenu		lcl_menu = new JMenu("Wizards");
		
		for(String lstr_wizName : WizardManager.inst().getWizardKeys()) {
			lcl_menu.add(buildAMenuItem(lstr_wizName));
		}
		
		mcl_menu.add(lcl_menu);
	}

	public void actionPerformed(ActionEvent cl_e) {

		if(cl_e.getActionCommand().equals(EXPORT_CODE_REP)) {
			JMECodeRep.doExport(mcl_jmeObject);
			return;
		}
		
		if(cl_e.getActionCommand().equals(SAVE_JME_OBJECT)) {
			if(!(mcl_jmeObject instanceof Savable)) {
				SimpleAlert.doAlert("Cannot save objects which are not instances of Savable", mcl_menu);
				return; 
			}
			
			final	File	lcl_f = GenericUtils.getAFile(null, false, true);

			if(lcl_f == null) { return; }
			if(lcl_f.exists()) {
				lcl_f.delete();
			}

			GameTaskQueueManager.getManager().update(new Callable<Object>() {
				public Object call() throws Exception {
					try {
						BinaryExporter.getInstance().save((Savable)mcl_jmeObject, lcl_f);
					}
					catch (IOException e) {
						e.printStackTrace();
					}
					SimpleAlert.doAlert("Object Saved to " + lcl_f.toString(), mcl_menu);
					return null;
				}
			});
			return;
		}

		if(cl_e.getActionCommand().equals(REMOVE)) {
			if(mcl_jmeObject instanceof Spatial) {
				GameTaskQueueManager.getManager().update(new Callable<Object>() {
					public Object call() throws Exception {
						Spatial		lcl_s = (Spatial)mcl_jmeObject;
						boolean		lb_ok = lcl_s.removeFromParent();
						if(!lb_ok) {
							SimpleAlert.doAlert("Spatial could not be removed", mcl_menu);
						}

						return null;
					}
				});
			}
			else if(mcl_jmeObject instanceof Controller) {
				if(mcl_parent instanceof Spatial) {
					GameTaskQueueManager.getManager().update(new Callable<Object>() {
						public Object call() throws Exception {
							Spatial	lcl_s = (Spatial)mcl_parent;
							lcl_s.removeController((Controller)mcl_jmeObject);
							return null;
						}
					});
					
				}
			} else if(mcl_jmeObject instanceof RenderState) {
				GameTaskQueueManager.getManager().update(new Callable<Object>() {
					public Object call() throws Exception {
						if(mcl_parent instanceof Spatial) {
							Spatial	lcl_s = (Spatial)mcl_parent;
							RenderState		lcl_r = (RenderState)mcl_jmeObject;
							lcl_s.clearRenderState(lcl_r.getStateType());
							lcl_s.updateRenderState();
						} else if(mcl_parent instanceof Pass) {
							Pass		lcl_pass = (Pass)mcl_parent;
							RenderState		lcl_r = (RenderState)mcl_jmeObject;
							lcl_pass.clearPassState(lcl_r.getStateType());
						}
						
						return null;
					}
				});
			}
			return;
		}

		if(cl_e.getActionCommand().equals(CUT_OBJECT)) {
			mcl_menu.setObjectBuffer(mcl_jmeObject);
			GameTaskQueueManager.getManager().update(new Callable<Object>() {
				public Object call() throws Exception {
					boolean		lb_ok = ((Spatial)mcl_jmeObject).removeFromParent();
					if(!lb_ok) {
						SimpleAlert.doAlert("Spatial could not be removed", mcl_menu);
					}
					return null;
				}
			});
			return;
		}
		
		if(cl_e.getActionCommand().equals(COPY_OBJECT_DATA)) {
			if(mcl_jmeObject instanceof Spatial) {
				ByteArrayOutputStream		lcl_bs = new ByteArrayOutputStream();
				try {
					BinaryExporter.getInstance().save((Savable)mcl_jmeObject, lcl_bs);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				mcl_menu.setObjectBuffer(lcl_bs);
				
			} else if(mcl_jmeObject instanceof RenderState) {
				mcl_menu.setObjectBuffer(mcl_jmeObject);
			}
			
			return;
		}
		
		if(cl_e.getActionCommand().equals(COPY_OBJECT_REFERENCE)) {
			mcl_menu.setObjectBuffer(mcl_jmeObject);
			return;
		}
		
		
		if(cl_e.getActionCommand().equals(PASTE_OBJECT)) {

			GameTaskQueueManager.getManager().update(new Callable<Object>(){

				public Object call() throws Exception {
					ObjectPasteAction		lcl_action;

					Class<?>	lcl_class = mcl_jmeObject.getClass();
					lcl_action = mcla_pasteActions.get(lcl_class);

					while((lcl_action == null) && (lcl_class != null)) {
						lcl_class = lcl_class.getSuperclass();
						lcl_action = mcla_pasteActions.get(lcl_class);
					}

					if(lcl_action == null) { return null; }

					if(mcl_menu.getObjectBuffer() instanceof ByteArrayOutputStream) {
						ByteArrayOutputStream		lcl_out = (ByteArrayOutputStream)mcl_menu.getObjectBuffer();
						byte						lcla_buf[] = lcl_out.toByteArray();
						ByteArrayInputStream		lcl_in = new ByteArrayInputStream(lcla_buf);
						Savable		lcl_s = BinaryImporter.getInstance().load(lcl_in);
						lcl_action.pasteObject(lcl_s, mcl_jmeObject);
					} else {
						lcl_action.pasteObject(mcl_menu.getObjectBuffer(), mcl_jmeObject);
					}
					
					mcl_menu.setObjectBuffer(null);

					return null;
				}

			});
		}
		
		doWizardSelection(cl_e.getActionCommand());
	}
	
	private	void	doWizardSelection(String str_actionCommand) {
		// check to see if this is a wizard entry
		Class<? extends WizardBase>		lcl_wizClass = WizardManager.inst().getWizardClass(str_actionCommand);
		if(lcl_wizClass != null) {
			try {
				Constructor<?>			lcl_ctor = lcl_wizClass.getConstructor(JDialog.class);
				
				final	JDialog		lcl_dlg = SceneWorkerDialogManager.inst().makeADialog(SceneWorkerDialogManager.WIZARD);
				lcl_dlg.setLayout(new MigLayout("inset 5"));
				final	WizardBase		lcl_wiz = (WizardBase)lcl_ctor.newInstance(lcl_dlg);
				lcl_wiz.doWizardUI(mcl_jmeObject);
				lcl_dlg.add(lcl_wiz, "wrap");
				
				JButton		lcl_exe = SwingComponentFactory.makeAButton("Execute");
				lcl_exe.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						lcl_wiz.executeWizard(mcl_jmeObject);
						lcl_dlg.dispose();
					}
				});
				
				JButton		lcl_cancel = SwingComponentFactory.makeAButton("Cancel");
				lcl_cancel.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						lcl_dlg.dispose();
					}
				});
				MigLayoutUtils.makeRightAlignedButtonBar(lcl_dlg, lcl_exe, lcl_cancel);
				
				lcl_dlg.pack();
				lcl_dlg.setVisible(true);
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public	interface	ObjectPasteAction {
		void	pasteObject(Object cl_objectToPaste, Object cl_objectToPasteTo);
	}
}
